package com.onpositive.data.serializer.readers;

import java.io.IOException;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.BitSet;

import javaewah.EWAHCompressedBitmap;
import javaewah.IntIterator;

import com.onpositive.data.AbstractReader;

abstract public class CollectionReader<T> extends AbstractReader<T> {
	
	protected BitSet emptyCells;
	
	private int dataOffset , sizeInBytes, itemSize ;

	protected int size;
	
	
	public CollectionReader(Class<T> clazz )
	{
		super( clazz ) ;
	}

	

	@SuppressWarnings("unchecked")
	protected void doInit() {
		
		int initialReadSize = INT_SIZE * 4 ; 
		
		try {			
			this.file.seek( collectionStart + myStart + INT_SIZE ) ; //also skip writerID
			
			byte[] byteArr = new byte[ initialReadSize ] ;
			
			file.read( byteArr, 0, initialReadSize ) ;
			ByteBuffer bBuf = ByteBuffer.wrap( byteArr ) ;
			IntBuffer iBuf = bBuf.asIntBuffer() ;
			
			sizeInBytes = iBuf.get() ;
			itemSize = iBuf.get() ;
			int nontrivialValuesCount = iBuf.get() ;
			int emptyCellsSize = iBuf.get() ;
			
			dataOffset = INT_SIZE * ( 5 + emptyCellsSize ) ;
			
			EWAHCompressedBitmap emptyCellsBitMap = new EWAHCompressedBitmap() ;
			emptyCellsBitMap.deserialize( file ) ;
			
			size = nontrivialValuesCount + emptyCellsBitMap.cardinality() ;
			
			long t0 = System.currentTimeMillis() ;
			emptyCells = new BitSet( emptyCellsBitMap.cardinality() ) ;			
			for( IntIterator it = emptyCellsBitMap.intIterator() ; it.hasNext() ; ){
				int ind = it.next() ;
				emptyCells.set( ind ) ;
			}
			long t1 = System.currentTimeMillis() ;			
			super.bitmapInitTime = t1 - t0 ;
			
			
			byte[] byteData = new byte[ nontrivialValuesCount * itemSize ] ;
			file.read( byteData, 0, nontrivialValuesCount * itemSize ) ;
			
			t0= System.currentTimeMillis() ;						
			initValuesAray( byteData ) ;
			t1= System.currentTimeMillis() ;
			super.valuesArrayInitTime = t1 - t0 ;
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	protected void initValuesAray(byte[] byteData) {
		
		T[] valuesArray = (T[]) Array.newInstance( super.getClazz(), size ) ;
		
		ByteBuffer bBuf = ByteBuffer.wrap( byteData ) ;
		
		boolean continueIteration = true ;
		int start = 0 ;
		int end = emptyCells.nextSetBit(0) ;
		for(  ; continueIteration ;  )
		{			
			for( int i = start ; i < end ; i++ )
				valuesArray[i] = readObject( bBuf ) ;				

			if( end < size )
				valuesArray[end] = defaultObject() ;

			start = end+1 ;
			end = emptyCells.nextSetBit( start ) ;
			end = end >= 0 ? end : size ;
			continueIteration = start <= size ;			
		}
		
		//dataRef = new WeakReference<T[]>( valuesArray ) ;
	}	

	@Override
	public T getObject(int i) {		
		
		if( !isInitialized() )
			init() ;
		
		if ( emptyCells.get(i) )
			return null ;
		try{
		return doGet(i) ;
		}catch (Exception e) {
			return getObject(i);
		}
	}
	
	@SuppressWarnings("unchecked")
	protected abstract  T doGet( int i );
	
	
	
	abstract protected T readObject(ByteBuffer bBuf) ;
	abstract protected T defaultObject() ;

	
}
